/**
 * 
 */
package ro.dta.idbi.ui.patterns;

import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;

import ro.dta.idbi.interfaces.sql.ISqlColumn;
import ro.dta.idbi.interfaces.sql.ISqlConstruct;
import ro.dta.idbi.interfaces.sql.ISqlForeignKey;
import ro.dta.idbi.interfaces.sql.ISqlTable;
import ro.dta.idbi.interfaces.ui.IMainFrame;
import ro.dta.idbi.model.Utils;
import ro.dta.idbi.ui.sql.SqlColumnCell;
import ro.dta.idbi.ui.sql.SqlForeignKeyCell;
import ro.dta.idbi.ui.sql.SqlGraph;
import ro.dta.idbi.ui.sql.SqlTableCell;
import uk.ac.ic.doc.automed.reps.Schema;

import com.mxgraph.model.mxGeometry;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxEvent;
import com.mxgraph.util.mxEventObject;
import com.mxgraph.util.mxEventSource.mxIEventListener;
import com.mxgraph.view.mxGraph;

/**
 * UI for the rename transformation pattern
 * 
 * @author Tudor Dobrila
 * 
 */
public class RenamePrimitiveDialog extends AbstractPatternDialog {

	private static final long serialVersionUID = -5116383487952416931L;

	private final static Logger LOGGER = Logger.getLogger("ro.dta.idbi.ui.patterns.RenameDialog");

	/**
	 * Default new name of the renamed element
	 */
	private final static String DEFAULT_NAME = "newName";

	/**
	 * Old name of the construct
	 */
	private transient String oldName;

	/**
	 * UI of the modified element, in the "After transformation" panel
	 */
	private transient ISqlConstruct newConstruct;

	/**
	 * Check if the current pattern can be applied over a given set of objects
	 * 
	 * Used to check if the items selected by the user in the UI can be used to
	 * apply this transformation pattern over them
	 * 
	 * @param schema
	 *            Schema in the context of which to check the validity of the
	 *            array of objects
	 * @param objects
	 *            Set of objects
	 * @return True if pattern can be applied over set of objects, false
	 *         otherwise
	 */
	public static boolean isValid(final Schema schema, final Object[] objects) {
		return objects.length == 1
				&& (objects[0] instanceof ISqlTable || objects[0] instanceof ISqlColumn || objects[0] instanceof ISqlForeignKey);
	}

	/**
	 * Rename pattern dialog class constructor
	 * 
	 * @param parentFrame
	 *            Parent frame of this dialog
	 * @param schema
	 *            Schema over which the transformation is applied
	 * @param element
	 *            Sql construct(s) over which the transformation is applied
	 * @param beforeLabel
	 *            Label for the before diagram
	 * @param afterLabel
	 *            Label for the transformed diagram
	 */
	public RenamePrimitiveDialog(final IMainFrame parentFrame, final Schema schema,
			final ISqlConstruct[] element, final JLabel beforeLabel, final JLabel afterLabel) {
		super(parentFrame, schema, element, beforeLabel, afterLabel);

		if (!isValid(schema, element)) {
			throw new IllegalArgumentException(
					"This pattern can't be applied to the given elements");
		}

		updateMacro(DEFAULT_NAME);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see ro.dta.idbi.ui.patterns.TransformationDialog#createOriginalDiagram()
	 */
	@Override
	protected mxGraphComponent createOriginalDiagram() {
		final SqlGraph graph = new SqlGraph();
		graph.getModel().beginUpdate();

		try {
			if (element[0] instanceof SqlTableCell) {
				SqlTableCell newTable = (SqlTableCell) element[0].clone();
				newTable.setStyle("SEL_TABLE");
				addTable(graph, newTable, new mxGeometry(5, 5, element[0]
						.getGeometry().getWidth(), element[0].getGeometry().getHeight()));
			} else if (element[0] instanceof SqlColumnCell) {
				final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) element[0])
						.getParent();
				SqlTableCell newTable = (SqlTableCell) parentTable.clone();
				final ISqlColumn col = newTable.getColumn(element[0].getValue().toString());
				col.setStyle("SEL_COL");
				addTable(graph, newTable, new mxGeometry(5, 5, parentTable
						.getGeometry().getWidth(), parentTable.getGeometry().getHeight()));
			} else if (element[0] instanceof SqlForeignKeyCell) {
				SqlForeignKeyCell fkCell = (SqlForeignKeyCell) element[0];
				final SqlTableCell sourceTable = (SqlTableCell) (fkCell.getSource()).clone();
				addTable(graph, sourceTable, new mxGeometry(5, 5, sourceTable.getGeometry()
						.getWidth(), sourceTable.getGeometry().getHeight()));

				final SqlTableCell targetTable = (SqlTableCell) (fkCell.getTarget()).clone();
				addTable(graph, targetTable, new mxGeometry(5 + sourceTable.getGeometry()
						.getWidth() + 125, 5, targetTable.getGeometry().getWidth(), targetTable
						.getGeometry().getHeight()));

				final ISqlForeignKey fk = new SqlForeignKeyCell(null, null, "", element[0]
						.getValue().toString(), sourceTable, targetTable, "FK_EDGE");
				graph.addCell(fk);
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.setAutoSizeCells(true);
			graph.getModel().endUpdate();
		}

		return new mxGraphComponent(graph);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * ro.dta.idbi.ui.patterns.TransformationDialog#createTransformedDiagram()
	 */
	@Override
	protected mxGraphComponent createTransformedDiagram() {
		final SqlGraph graph = new EditableGraph();
		graph.getModel().beginUpdate();

		try {
			if (element[0] instanceof SqlTableCell) {
				// Renaming a table
				newConstruct = (SqlTableCell) element[0].clone();
				newConstruct.setStyle("SEL_TABLE");
				newConstruct.setValue(DEFAULT_NAME);
				addTable(graph, (SqlTableCell) newConstruct, new mxGeometry(5, 5, element[0]
						.getGeometry().getWidth(), element[0].getGeometry().getHeight()));
			} else if (element[0] instanceof SqlColumnCell) {
				// Renaming a column
				final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) element[0])
						.getParent();
				SqlTableCell newTable = (SqlTableCell) parentTable.clone();
				final ISqlColumn col = newTable.getColumn(element[0].getValue().toString());
				col.setStyle("SEL_COL");
				col.setValue(DEFAULT_NAME);
				newConstruct = col;
				addTable(graph, newTable,
						new mxGeometry(5, 5, parentTable.getGeometry().getWidth(), parentTable
								.getGeometry().getHeight()));
			} else if (element[0] instanceof SqlForeignKeyCell) {
				// Renaming a foreign key
				SqlForeignKeyCell fkCell = (SqlForeignKeyCell) element[0];
				final SqlTableCell sourceTable = (SqlTableCell) (fkCell.getSource()).clone();
				addTable(graph, sourceTable, new mxGeometry(5, 5, sourceTable.getGeometry()
						.getWidth(), sourceTable.getGeometry().getHeight()));

				final SqlTableCell targetTable = (SqlTableCell) (fkCell.getTarget()).clone();
				addTable(graph, targetTable, new mxGeometry(5 + sourceTable.getGeometry()
						.getWidth() + 125, 5, targetTable.getGeometry().getWidth(), targetTable
						.getGeometry().getHeight()));

				final ISqlForeignKey fk = new SqlForeignKeyCell(null, null, "", DEFAULT_NAME,
						sourceTable, targetTable, "SEL_FK");
				newConstruct = fk;
				graph.addCell(fk);
			}
		} catch (CloneNotSupportedException e) {
			LOGGER.log(Level.SEVERE, "Fatal exception", e);
		} finally {
			graph.getModel().endUpdate();
		}

		final mxGraphComponent graphComponent = new mxGraphComponent(graph);
		graphComponent.setEnterStopsCellEditing(true);
		graphComponent.addListener(mxEvent.LABEL_CHANGED, new mxIEventListener() {

			@Override
			public void invoke(final Object arg0, final mxEventObject arg1) {
				updateMacro(newConstruct.getValue().toString());
			}
		});

		graphComponent.startEditingAtCell(newConstruct);
		return graphComponent;
	}

	/**
	 * Add a table passed as parameter to the graph passed as parameter, using
	 * the geometry passed as paramter
	 * 
	 * @param graph
	 *            Graph where to add the table
	 * @param table
	 *            Table to add to the graph
	 * @param geometry
	 *            Geometry used when adding the table to the graph
	 */
	private void addTable(mxGraph graph, SqlTableCell table, mxGeometry geometry) {
		if (table != null) {
			table.setGeometry(geometry);
			table.layout(graph);
		}
	}

	/**
	 * Update the macro header, given the new name of the object being modified
	 * 
	 * @param newObjectName
	 *            New name of the object being renamed
	 */
	private void updateMacro(String newObjectName) {
		String newName = "";
		if (element[0] instanceof SqlTableCell) {
			// Renaming a table
			oldName = element[0].getSchemeDefinition();
			newName = "table:<<" + newObjectName + ">>";
		} else if (element[0] instanceof SqlColumnCell) {
			// Renaming a column
			final SqlTableCell parentTable = (SqlTableCell) ((SqlColumnCell) element[0])
					.getParent();
			oldName = "column:" + ((SqlColumnCell) element[0]).getShortDefinition();
			newName = "column:<<" + parentTable.getValue().toString() + "," + newObjectName + ">>";
		} else if (element[0] instanceof SqlForeignKeyCell) {
			// Renaming a foreign key
			ISqlForeignKey fk = (ISqlForeignKey) element[0];
			oldName = fk.getSchemeDefinition();
			newName = Utils.genFKRepresentation(newObjectName, (ISqlTable) fk.getSource(),
					(ISqlTable) fk.getTarget(), fk.getSourceCols(), fk.getTargetCols());
		}

		macroField.setText("rename (" + oldName + "," + newName + ")");
	}

}
